From 71adf3c9eb195fd0c9105781df8a1290f4d87154 Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Tue, 15 May 2007 10:28:28 +0100 Subject: [PATCH] x86/hvm: hypercall adjustments - share more code between 32- and 64-bit variants - properly handle continuations for 32-bit guests on 64-bit hv - properly handle preemption (this must *not* rely on regs->eip, as - other code may overwrite the value there by calling - hvm_store_cpu_guest_regs() - deny hypercall access when called from guest in vm86 mode, which requires that ???_guest_x86_mode() make real and vm86 modes distinguishable Signed-off-by: Jan Beulich --- xen/arch/x86/domain.c | 9 ++- xen/arch/x86/hvm/hvm.c | 111 ++++++++++++++------------------ xen/arch/x86/hvm/platform.c | 3 + xen/arch/x86/hvm/svm/svm.c | 24 +++---- xen/arch/x86/hvm/vmx/vmx.c | 25 ++----- xen/include/asm-x86/hypercall.h | 9 +++ 6 files changed, 83 insertions(+), 98 deletions(-) diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 07c26ba953..8267c5043b 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1231,6 +1232,8 @@ void sync_vcpu_execstate(struct vcpu *v) __arg; \ }) +DEFINE_PER_CPU(char, hc_preempted); + unsigned long hypercall_create_continuation( unsigned int op, const char *format, ...) { @@ -1262,7 +1265,9 @@ unsigned long hypercall_create_continuation( regs->eip -= 2; /* re-execute 'syscall' / 'int 0x82' */ #ifdef __x86_64__ - if ( !is_pv_32on64_domain(current->domain) ) + if ( !is_hvm_vcpu(current) ? + !is_pv_32on64_vcpu(current) : + (hvm_guest_x86_mode(current) == 8) ) { for ( i = 0; *p != '\0'; i++ ) { @@ -1298,6 +1303,8 @@ unsigned long hypercall_create_continuation( } } } + + this_cpu(hc_preempted) = 1; } va_end(args); diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 1da7596d5f..0f042ea4f8 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -663,7 +663,7 @@ typedef unsigned long hvm_hypercall_t( #if defined(__i386__) -static hvm_hypercall_t *hvm_hypercall_table[NR_hypercalls] = { +static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = { HYPERCALL(memory_op), HYPERCALL(multicall), HYPERCALL(xen_version), @@ -672,21 +672,6 @@ static hvm_hypercall_t *hvm_hypercall_table[NR_hypercalls] = { HYPERCALL(hvm_op) }; -static void __hvm_do_hypercall(struct cpu_user_regs *pregs) -{ - if ( (pregs->eax >= NR_hypercalls) || !hvm_hypercall_table[pregs->eax] ) - { - if ( pregs->eax != __HYPERVISOR_grant_table_op ) - gdprintk(XENLOG_WARNING, "HVM vcpu %d:%d bad hypercall %d.\n", - current->domain->domain_id, current->vcpu_id, pregs->eax); - pregs->eax = -ENOSYS; - return; - } - - pregs->eax = hvm_hypercall_table[pregs->eax]( - pregs->ebx, pregs->ecx, pregs->edx, pregs->esi, pregs->edi); -} - #else /* defined(__x86_64__) */ static long do_memory_op_compat32(int cmd, XEN_GUEST_HANDLE(void) arg) @@ -746,49 +731,38 @@ static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = { HYPERCALL(hvm_op) }; -static void __hvm_do_hypercall(struct cpu_user_regs *pregs) -{ - pregs->rax = (uint32_t)pregs->eax; /* mask in case compat32 caller */ - if ( (pregs->rax >= NR_hypercalls) || !hvm_hypercall64_table[pregs->rax] ) - { - if ( pregs->rax != __HYPERVISOR_grant_table_op ) - gdprintk(XENLOG_WARNING, "HVM vcpu %d:%d bad hypercall %ld.\n", - current->domain->domain_id, current->vcpu_id, pregs->rax); - pregs->rax = -ENOSYS; - return; - } - - if ( current->arch.paging.mode->guest_levels == 4 ) - { - pregs->rax = hvm_hypercall64_table[pregs->rax](pregs->rdi, - pregs->rsi, - pregs->rdx, - pregs->r10, - pregs->r8); - } - else - { - pregs->eax = hvm_hypercall32_table[pregs->eax]((uint32_t)pregs->ebx, - (uint32_t)pregs->ecx, - (uint32_t)pregs->edx, - (uint32_t)pregs->esi, - (uint32_t)pregs->edi); - } -} - #endif /* defined(__x86_64__) */ int hvm_do_hypercall(struct cpu_user_regs *regs) { - int flush, preempted; - unsigned long old_eip; + int flush, mode = hvm_guest_x86_mode(current); + uint32_t eax = regs->eax; - hvm_store_cpu_guest_regs(current, regs, NULL); + switch ( mode ) + { +#ifdef __x86_64__ + case 8: +#endif + case 4: + case 2: + hvm_store_cpu_guest_regs(current, regs, NULL); + if ( unlikely(ring_3(regs)) ) + { + default: + regs->eax = -EPERM; + return HVM_HCALL_completed; + } + case 0: + break; + } - if ( unlikely(ring_3(regs)) ) + if ( (eax >= NR_hypercalls) || !hvm_hypercall32_table[eax] ) { - regs->eax = -EPERM; - return 0; + if ( eax != __HYPERVISOR_grant_table_op ) + gdprintk(XENLOG_WARNING, "HVM vcpu %d:%d bad hypercall %u.\n", + current->domain->domain_id, current->vcpu_id, eax); + regs->eax = -ENOSYS; + return HVM_HCALL_completed; } /* @@ -796,20 +770,29 @@ int hvm_do_hypercall(struct cpu_user_regs *regs) * For now we also need to flush when pages are added, as qemu-dm is not * yet capable of faulting pages into an existing valid mapcache bucket. */ - flush = ((uint32_t)regs->eax == __HYPERVISOR_memory_op); - - /* Check for preemption: RIP will be modified from this dummy value. */ - old_eip = regs->eip; - regs->eip = 0xF0F0F0FF; + flush = (eax == __HYPERVISOR_memory_op); + this_cpu(hc_preempted) = 0; - __hvm_do_hypercall(regs); - - preempted = (regs->eip != 0xF0F0F0FF); - regs->eip = old_eip; - - hvm_load_cpu_guest_regs(current, regs); +#ifdef __x86_64__ + if ( mode == 8 ) + { + regs->rax = hvm_hypercall64_table[eax](regs->rdi, + regs->rsi, + regs->rdx, + regs->r10, + regs->r8); + } + else +#endif + { + regs->eax = hvm_hypercall32_table[eax]((uint32_t)regs->ebx, + (uint32_t)regs->ecx, + (uint32_t)regs->edx, + (uint32_t)regs->esi, + (uint32_t)regs->edi); + } - return (preempted ? HVM_HCALL_preempted : + return (this_cpu(hc_preempted) ? HVM_HCALL_preempted : flush ? HVM_HCALL_invalidate : HVM_HCALL_completed); } diff --git a/xen/arch/x86/hvm/platform.c b/xen/arch/x86/hvm/platform.c index 7bad53ab1a..2b173c18de 100644 --- a/xen/arch/x86/hvm/platform.c +++ b/xen/arch/x86/hvm/platform.c @@ -1037,6 +1037,9 @@ void handle_mmio(unsigned long gpa) df = regs->eflags & X86_EFLAGS_DF ? 1 : 0; address_bytes = hvm_guest_x86_mode(v); + if (address_bytes < 2) + /* real or vm86 modes */ + address_bytes = 2; inst_addr = hvm_get_segment_base(v, x86_seg_cs) + regs->eip; inst_len = hvm_instruction_length(inst_addr, address_bytes); if ( inst_len <= 0 ) diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index ca1b672bbe..9efa866158 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -554,14 +554,6 @@ static inline void svm_restore_dr(struct vcpu *v) __restore_debug_registers(v); } -static int svm_realmode(struct vcpu *v) -{ - unsigned long cr0 = v->arch.hvm_svm.cpu_shadow_cr0; - unsigned long eflags = v->arch.hvm_svm.vmcb->rflags; - - return (eflags & X86_EFLAGS_VM) || !(cr0 & X86_CR0_PE); -} - static int svm_interrupts_enabled(struct vcpu *v) { unsigned long eflags = v->arch.hvm_svm.vmcb->rflags; @@ -572,13 +564,13 @@ static int svm_guest_x86_mode(struct vcpu *v) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; - if ( svm_long_mode_enabled(v) && vmcb->cs.attr.fields.l ) + if ( unlikely(!(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_PE)) ) + return 0; + if ( unlikely(vmcb->rflags & X86_EFLAGS_VM) ) + return 1; + if ( svm_long_mode_enabled(v) && likely(vmcb->cs.attr.fields.l) ) return 8; - - if ( svm_realmode(v) ) - return 2; - - return (vmcb->cs.attr.fields.db ? 4 : 2); + return (likely(vmcb->cs.attr.fields.db) ? 4 : 2); } static void svm_update_host_cr3(struct vcpu *v) @@ -1950,7 +1942,9 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type, case INSTR_SMSW: value = v->arch.hvm_svm.cpu_shadow_cr0 & 0xFFFF; modrm = buffer[index+2]; - addr_size = svm_guest_x86_mode( v ); + addr_size = svm_guest_x86_mode(v); + if ( addr_size < 2 ) + addr_size = 2; if ( likely((modrm & 0xC0) >> 6 == 3) ) { gpreg = decode_src_reg(prefix, modrm); diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index dfc4ad6431..e245cc79a5 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -994,31 +994,20 @@ static void vmx_init_hypercall_page(struct domain *d, void *hypercall_page) *(u16 *)(hypercall_page + (__HYPERVISOR_iret * 32)) = 0x0b0f; /* ud2 */ } -static int vmx_realmode(struct vcpu *v) -{ - unsigned long rflags; - - ASSERT(v == current); - - rflags = __vmread(GUEST_RFLAGS); - return rflags & X86_EFLAGS_VM; -} - static int vmx_guest_x86_mode(struct vcpu *v) { - unsigned long cs_ar_bytes; + unsigned int cs_ar_bytes; ASSERT(v == current); + if ( unlikely(!(v->arch.hvm_vmx.cpu_shadow_cr0 & X86_CR0_PE)) ) + return 0; + if ( unlikely(__vmread(GUEST_RFLAGS) & X86_EFLAGS_VM) ) + return 1; cs_ar_bytes = __vmread(GUEST_CS_AR_BYTES); - - if ( vmx_long_mode_enabled(v) && (cs_ar_bytes & (1u<<13)) ) + if ( vmx_long_mode_enabled(v) && likely(cs_ar_bytes & (1u<<13)) ) return 8; - - if ( vmx_realmode(v) ) - return 2; - - return ((cs_ar_bytes & (1u<<14)) ? 4 : 2); + return (likely(cs_ar_bytes & (1u<<14)) ? 4 : 2); } static int vmx_pae_enabled(struct vcpu *v) diff --git a/xen/include/asm-x86/hypercall.h b/xen/include/asm-x86/hypercall.h index fa9476c1d3..374f995aa6 100644 --- a/xen/include/asm-x86/hypercall.h +++ b/xen/include/asm-x86/hypercall.h @@ -15,6 +15,15 @@ */ #define MMU_UPDATE_PREEMPTED (~(~0U>>1)) +/* + * This gets set to a non-zero value whenever hypercall_create_continuation() + * is used (outside of multicall context; in multicall context the second call + * from do_multicall() itself will have this effect). Internal callers of + * hypercall handlers interested in this condition must clear the flag prior + * to invoking the respective handler(s). + */ +DECLARE_PER_CPU(char, hc_preempted); + extern long do_event_channel_op_compat( XEN_GUEST_HANDLE(evtchn_op_t) uop); -- 2.30.2